package com.wyz.saas.common.audio

import android.media.AudioManager
import android.media.MediaPlayer
import java.io.IOException

/**
 * 语音播放工具类，可以播放一个本地的文件
 * 当进行一个语音的播放的时候，如果想要播放一个新的文件，
 * 则会自动把上一个未播放完成的语音停止掉，并回调停止方法
 * 在这里我们接收一个范型Holder，相当于一个Tag的作用
 * 该范型主要用于回调播放状态时标示对应的目标
 */
class AudioPlayHelper<Holder> : MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {

    /**
     * 当前的播放监听器
     * @param <Holder> 任意范型的目标Holder</Holder>
     */
    interface RecordPlayListener<Holder> {
        /**
         * 当播放开始时
         */
        fun onPlayStart(holder: Holder)

        /**
         * 当播放停止时
         */
        fun onPlayStop(holder: Holder)

        /**
         * 当播放移除时
         */
        fun onPlayError(holder: Holder)

    }

    // 系统的播放器
    private var mediaPlayer: MediaPlayer? = null

    // 全局的播放状态监听器，用于回调当前的播放状态
    private val listener: RecordPlayListener<Holder>

    // 范型的一个目标，当前播放的目标，可以是任意的类型
    private var holder: Holder? = null

    // 当前的播放地址
    private var currentPath: String? = null

    /**
     * 构造放方法
     */
    constructor(listener: RecordPlayListener<Holder>) {
        this.listener = listener
        // 进行播放器创建
        this.mediaPlayer = createNewMediaPlayer()
    }

    /**
     * 创建一个新的播放器
     *
     * @return MediaPlayer
     */
    private fun createNewMediaPlayer(): MediaPlayer {
        val mediaPlayer = MediaPlayer()
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC)
        // 不循环播放
        mediaPlayer.isLooping = false
        // 设置播放错误时的回调为当前类
        mediaPlayer.setOnErrorListener(this)
        // 设置播放完成的相关监听
        mediaPlayer.setOnCompletionListener(this)
        return mediaPlayer
    }

    /**
     * Trigger 方法与 play 以及 stop 方法都不同，他具备播放与停止的职责
     * 当你点击播放一个语音时，如果在播放中你又点击了一次这个播放的语音，那么会进行停止操作
     * 当你点击一个不同于当前的语音进行播放时则会进行正常的播放流程
     * @param holder   目标
     * @param filePath 播放的语音地址
     */
    fun trigger(holder: Holder, filePath: String?) {
        // 判断播放器状态
        val play = mediaPlayer ?: return
        // 如果当前正在播放，而且播放的就是点击进来的语音，则代表想要停止这个语音
        if (play.isPlaying && filePath.equals(this.currentPath, ignoreCase = true)) {
            stop()
            return
        }
        // 播放前先尝试进行停止操作
        stop()
        // 播放一个新的语音
        play(holder, filePath)
    }

    /**
     * 播放一个语音
     *
     * @param holder   目标
     * @param filePath 播放的语音的地址
     */
    private fun play(holder: Holder, filePath: String?) {
        // 判断播放器状态
        val play = mediaPlayer ?: return
        // 设置当前的目标与地址
        this.holder = holder
        this.currentPath = filePath

        try {
            // 设置播放地址，并准备
            play.setDataSource(filePath)
            play.prepare()
        } catch (e: IOException) {
            e.printStackTrace()
            // 错误情况下直接回调播放错误
            listener.onPlayError(holder)
            // 同时进行停止操作
            stop()
            return
        }
        // 开始播放
        play.start()
        // 回调开始播放的状态
        listener.onPlayStart(holder)
    }

    /**
     * 进行停止操作
     */
    private fun stop() {
        // 判断播放器状态
        val play = mediaPlayer ?: return
        // 停止播放器，并重置状态
        play.stop()
        play.reset()
        currentPath = null
        // 如果当前的目标不等于null则回调一次播放停止回调
        val holder: Holder? = holder
        // 仅仅只能回调一次停止，所以需要马上赋值null
        this.holder = null
        if (holder != null) {
            listener.onPlayStop(holder)
        }
    }

    /**
     * 进行销毁的操作，如果退出界面则应当释放掉播放器
     */
    fun destroy() {
        // 判断播放器状态
        val play = mediaPlayer ?: return
        stop()
        play.release()
        this.mediaPlayer = null
    }

    override fun onCompletion(mp: MediaPlayer?) {
        // 播放器的播放完成回调，在这里我们回调用户播放完成了
        stop()
    }

    override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
        // 错误情况下，回调错误并进行停止的流程
        val holder = holder
        if (holder != null) {
            listener.onPlayError(holder)
        }
        stop()
        return true
    }
}